class IUP_VAL
-- Creates a Valuator control. Selects a value in a limited interval. Also 
-- known as Scale or Trackbar in native systems.
--
-- In Motif, after the user clicks the handler a KILLFOCUS will be ignored when 
-- the control loses its focus.
--
-- Keyboard Mapping
--
-- This is the default mapping when horizontal and INVERTED has the default 
-- value (False):
--
-- Keys 	                     Action for HORIZONTAL
-- ============================================================
-- Right Arrow 	            move right, increment by one step
-- Left Arrow 	               move left, decrement by one step
-- Ctrl+Right Arrow or PgDn   move right, increment by one page step
-- Ctrl+Left Arrow or PgUp    move left, decrement by one page step
-- Home 	                     move all left, set to minimum
-- End 	                     move all right, set to maximum
--
--
-- This is the default mapping when vertical and INVERTED has the default 
-- value (True):
--
-- Keys 	                     Action for VERTICAL
-- ============================================================
-- Up Arrow                   move up, increment by one step
-- Down Arrow                 move down, decrement by one step
-- Ctrl+Up Arrow or PgUp 	   move up, increment by one page step
-- Ctrl+Down Arrow or PgDn    move down, decrement by one page step
-- Home   	                  move all up, set to maximum
-- End 	                     move all down, set to minimum
--
-- Visually all the keys move to the same direction independent from the 
-- INVERTED attribute.
--
-- Semantically all the keys change the value depending on the INVERTED 
-- attribute.
--
-- This behavior is slightly different from the defined by the native systems 
-- (Home and End keys are different). But it is the same in all systems.
	
inherit
	IUP_WIDGET
		redefine
			execute_map,
			execute_unmap,
			execute_destroy,
			execute_getfocus,
			execute_killfocus,
			execute_enterwindow,
			execute_leavewindow,
			execute_k_any,
			execute_help,
			execute_valuechanged
		end
	IUP_WIDGET_BGCOLOR
	IUP_WIDGET_RASTERSIZE
	IUP_WIDGET_USERSIZE
	IUP_WIDGET_ACTIVE
	IUP_WIDGET_EXPAND
	IUP_WIDGET_FONT
	IUP_WIDGET_SCREENPOSITION
	IUP_WIDGET_POSITION
	IUP_WIDGET_MAXMIN_SIZE
	IUP_WIDGET_TIP
	IUP_WIDGET_SIZE
	IUP_WIDGET_ZORDER
	IUP_WIDGET_VISIBLE
	IUP_WIDGET_CHILD
	IUP_WIDGET_NAME
	IUP_WIDGET_FOCUS
	IUP_WIDGET_PROPAGATEFOCUS
	IUP_WIDGET_CUSTOM_ATTRIBUTES

create {ANY}
   horizontal_val,
   vertical_val

feature {ANY}

	horizontal_val
		local
			a_val: POINTER
			orientation: STRING
		do
			orientation := "HORIZONTAL"
			a_val := int_val(get_pointer(orientation.to_c))
			set_widget(a_val)

			minv := 0
			maxv := 1
		end

	vertical_val
		local
			a_val: POINTER
			orientation: STRING
		do
			orientation := "VERTICAL"
			a_val := int_val(get_pointer(orientation.to_c))
			set_widget(a_val)

			minv := 0
			maxv := 1
		end

	-- Attributes

	set_can_focus (state: BOOLEAN)
		-- (creation only) (non inheritable): enables the focus traversal of the 
		-- control. In Windows the control will still get the focus when 
		-- clicked. Default: True.
		do
			iup_open.set_attribute(Current, "CANFOCUS", boolean_to_yesno(state))
		end

	set_inverted (state: BOOLEAN)
		-- Invert the minimum and maximum positions on screen. When INVERTED=True 
		-- maximum is at top and left (minimum is bottom and right), when 
		-- INVERTED=False maximum is at bottom and right (minimum is top and 
		-- left). The initial value depends on the orientation, if vertical 
		-- default is True, if horizontal default is False.
		do
			iup_open.set_attribute(Current, "INVERTED", boolean_to_yesno(state))
		end

	set_max (max_value: REAL)
		-- Contains the maximum valuator value. Default is "1". When changed the 
		-- display will not be updated until a call to set_value.
		require
			minv < max_value
		do
			iup_open.set_attribute(Current, "MAX", max_value.out)

			-- Update the max value
			maxv := max_value
		ensure
			updated: maxv = max_value
		end

	set_min (min_value: REAL)
		-- Contains the minimum valuator value. Default is "0". When changed the 
		-- display will not be updated until a call to set_value.
		require
			maxv > min_value
		do
			iup_open.set_attribute(Current, "MIN", min_value.out)

			-- Update the min value
			minv := min_value
		ensure
			updated: minv = min_value
		end

	set_page_step (value: REAL)
		-- Controls the increment for pagedown and pageup keys. It is not the 
		-- size of the increment. The increment size is "pagestep*(max-min)", so 
		-- it must be 0<pagestep<1. Default is "0.1".
		require
			value >= 0
			value <= 1
		do
			iup_open.set_attribute(Current, "PAGESTEP", value.out)
		end

	set_show_ticks (value: INTEGER)
		-- [Windows and Motif Only]: The number of tick marks along the valuator 
		-- trail. Minimum value is "2". Default is "0", in this case the ticks 
		-- are not shown. It can not be changed to 0 from a non zero value, or 
		-- vice-versa, after the control is mapped. GTK does not support 
		-- ticks.
		require
			value >= 0
			value /= 1
		do
			iup_open.set_attribute(Current, "SHOWTICKS", value.out)
		end

	set_step (value: REAL)
		-- Controls the increment for keyboard control and the mouse wheel. It is 
		-- not the size of the increment. The increment size is "step*(max-min)", 
		-- so it must be 0<step<1. Default is "0.01".
		require
			value >= 0
			value <= 1
		do
			iup_open.set_attribute(Current, "STEP", value.out)
		end

	set_ticks_pos (value: STRING)
		-- [Windows Only] (creation only): Allows to position the ticks in both 
		-- sides (BOTH) or in the reverse side (REVERSE). Default: NORMAL. The 
		-- normal position for horizontal orientation is at the top of the 
		-- control, and for vertical orientation is at the left of the control. 
		-- In Motif, the ticks position is always normal.
		require
			is_valid_ticks_pos(value)
		do
			iup_open.set_attribute(Current, "TICKSPOS", value)
		end

	is_horizontal: BOOLEAN
		local
			str: STRING
		do
			str := iup_open.get_attribute(Current, "ORIENTATION")

			if str.is_equal("HORIZONTAL") then
				Result := True
			else
				Result := False
			end
		end

	is_vertical: BOOLEAN
		local
			str: STRING
		do
			str := iup_open.get_attribute(Current, "ORIENTATION")

			if str.is_equal("VERTICAL") then
				Result := True
			else
				Result := False
			end
		end

	set_value (value: REAL)
		-- (non inheritable): Contains a number between min and max, indicating 
		-- the valuator position. Default: "0.0".
		do
			iup_open.set_attribute(Current, "VALUE", value.out)
		end

	get_value: REAL
		-- The value.
		local
			str: STRING
		do
			str := iup_open.get_attribute(Current, "VALUE")

			if str.is_real then
				Result := str.to_real
			end
		end

	-- Callbacks

	-- Common
	set_cb_map (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Called right after an element is mapped and its attributes updated.
		local
			operation: INTEGER
		do
			cb_map := act
			
			if cb_map /= Void then
				operation := 1
			else
				operation := 0
			end
						
			iup_open.set_callback (Current, "MAP_CB", "NONEEDED", operation)
		end

	set_cb_unmap (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Called right before an element is unmapped.
		local
			operation: INTEGER
		do
			cb_unmap := act

			if cb_unmap /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "UNMAP_CB", "NONEEDED", operation)
		end

	set_cb_destroy (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Called right before an element is destroyed.
		local
			operation: INTEGER
		do
			cb_destroy := act

			if cb_destroy /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "DESTROY_CB", "NONEEDED", operation)
		end

	set_cb_get_focus (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Action generated when an element is given keyboard focus. 
		-- This callback is called after the KILLFOCUS_CB of the element 
		-- that loosed the focus. The {IUP}.get_focus function during the 
		-- callback returns the element that loosed the focus.
		local
			operation: INTEGER
		do
			cb_getfocus := act

			if cb_getfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "GETFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_kill_focus (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Action generated when an element loses keyboard focus. This 
		-- callback is called before the GETFOCUS_CB of the element that 
		-- gets the focus.
		local
			operation: INTEGER
		do
			cb_killfocus := act

			if cb_killfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "KILLFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_enter_window (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Action generated when the mouse enters the native element. 
		local
			operation: INTEGER
		do
			cb_enterwindow := act

			if cb_enterwindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "ENTERWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_leave_window (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Action generated when the mouse leaves the native element.
		local
			operation: INTEGER
		do
			cb_leavewindow := act

			if cb_leavewindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "LEAVEWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_k_any (act: detachable FUNCTION[TUPLE[IUP_VAL, INTEGER], STRING])
		-- Action generated when a keyboard event occurs.
		-- IUP_WIDGET the element that activated the event.
		-- INTEGER identifier of typed key. Please refer to the Keyboard 
		-- Codes table for a list of possible values.
		--
		-- Returns: If IUP_IGNORE is returned the key is ignored and not
		-- processed by the control and not propagated. If returns 
		-- IUP_CONTINUE, the key will be processed and the event will be
		-- propagated to the parent of the element receiving it, this is 
		-- the default behavior. If returns IUP_DEFAULT the key is processed 
		-- but it is not propagated. IUP_CLOSE will be processed. 
		local
			operation: INTEGER
		do
			cb_k_any := act

			if cb_k_any /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "K_ANY", "NONEEDED", operation)
		end

	set_cb_help (act: detachable PROCEDURE[TUPLE[IUP_VAL]])
		-- Action generated when the user press F1 at a control. In Motif 
		-- is also activated by the Help button in some workstations 
		-- keyboard.
		-- Returns: IUP_CLOSE will be processed.
		local
			operation: INTEGER
		do
			cb_help := act

			if cb_help /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "HELP_CB", "NONEEDED", operation)
		end

	-- Extra
	set_cb_value_changed (act: detachable FUNCTION[TUPLE[IUP_VAL], STRING])
		-- Called after the value was interactively changed by the user.
		local
			operation: INTEGER
		do
			cb_valuechanged := act

			if cb_valuechanged /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "VALUECHANGED_CB", "NONEEDED", operation)
		end

	-- Validations

	minv, maxv: REAL

	is_valid_ticks_pos (value: STRING): BOOLEAN
		do
			if value.is_equal("BOTH") or
				value.is_equal("REVERSE") or
				value.is_equal("NORMAL") then
				Result := True
			else
				Result := False
			end
		end

feature {IUP}

	execute_map: STRING
		do
			if attached cb_map as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_unmap: STRING
		do
			if attached cb_unmap as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_destroy: STRING
		do
			if attached cb_destroy as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_getfocus: STRING
		do
			if attached cb_getfocus as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_killfocus: STRING
		do
			if attached cb_killfocus as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_enterwindow: STRING
		do
			if attached cb_enterwindow as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_leavewindow: STRING
		do
			if attached cb_leavewindow as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_k_any (c: INTEGER): STRING
		do
			if attached cb_k_any as int_cb then
				Result := int_cb.item([Current, c])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_help
		do
			if attached cb_help as int_cb then
				int_cb.call([Current])
			end
		end

	execute_valuechanged: STRING
		do
			if attached cb_valuechanged as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end	

feature {NONE}
	
	-- For callbacks
	
	cb_map: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_unmap: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_destroy: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_getfocus: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_killfocus: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_enterwindow: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_leavewindow: detachable FUNCTION[TUPLE[IUP_VAL], STRING]
	cb_k_any: detachable FUNCTION[TUPLE[IUP_VAL, INTEGER], STRING]
	cb_help: detachable PROCEDURE[TUPLE[IUP_VAL]]

	cb_valuechanged: detachable FUNCTION[TUPLE[IUP_VAL], STRING]

			-- Internal

	int_val(orientation: POINTER): POINTER
		external
			"C inline use %"eiffel-iup.h%""
      alias
			"return IupVal ($orientation);"
      end

end

-- The MIT License (MIT)

-- Copyright (c) 2016, 2017, 2019, 2020 by German A. Arias

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in 
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
